﻿using System;
using System.Collections.Concurrent;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.IO;
using Development.Tools;
using System.Reflection;


namespace Development.Script
{
    /// <summary>
    /// cs 脚本
    /// </summary>
    public class CsRunTime :Singleton<CsRunTime>,IScriptRunTime,IDisposable
    {
        private readonly string _codeName = "Cs";

        private ConcurrentDictionary<string, dynamic> _scopes = new ConcurrentDictionary<string, dynamic>();
        private FileSystemWatcher _fileWatcher = null;
        public void LoadScript()
        {
            LoadScript(null);
        }
        public void LoadScript(string [] referencedAssemblies)
        {
            CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider();
            CompilerParameters objCompilerParameters = new CompilerParameters();
            if(referencedAssemblies != null)
            {
                objCompilerParameters.ReferencedAssemblies.AddRange(referencedAssemblies);
            }
            objCompilerParameters.GenerateExecutable = false;
            objCompilerParameters.GenerateInMemory = true;
            var files = ScriptUtil.GetScriptFiles(_codeName);
            CompilerResults result = objCSharpCodePrivoder.CompileAssemblyFromFile(objCompilerParameters, files.ToArray());
            if(result.Errors.Count != 0)
            {
                foreach(var er in result.Errors)
                {
                    Log.WriteLog(ELogLevel.LOG_ERROR, er.ToString());
                }
                return;
            }

            Assembly assembly = result.CompiledAssembly;
            var types =  assembly.GetTypes();
            foreach(var t in types)
            {
                _scopes.TryAdd(t.Name, t.CreateInstance());
            }
            AddFileWatcher(ScriptUtil.GetScriptPath(_codeName), "*.cs");
        }

        private void AddFileWatcher(string path, string filter)
        {
            if (_fileWatcher == null)
            {
                _fileWatcher = new FileSystemWatcher(path, filter);
                _fileWatcher.Changed += watcher_Changed;
                _fileWatcher.Created += watcher_Changed;
                _fileWatcher.EnableRaisingEvents = true;
            }

        }

        void watcher_Changed(object sender, FileSystemEventArgs e)
        {
            try
            {
                CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider();
                CompilerParameters objCompilerParameters = new CompilerParameters();
                objCompilerParameters.GenerateExecutable = false;
                objCompilerParameters.GenerateInMemory = true;
                CompilerResults result = objCSharpCodePrivoder.CompileAssemblyFromFile(objCompilerParameters, e.FullPath);
                if (result.Errors.Count != 0)
                {
                    foreach (var er in result.Errors)
                    {
                        Log.WriteLog(ELogLevel.LOG_ERROR, er.ToString());
                    }
                    return;
                }
                Assembly assembly = result.CompiledAssembly;
                var types = assembly.GetTypes();
                foreach (var t in types)
                {
                    if (_scopes.ContainsKey(t.Name))
                    {
                        _scopes[t.Name] = t.CreateInstance();
                    }
                    else
                    {
                        _scopes.TryAdd(t.Name, t.CreateInstance());
                    }
                    Log.WriteLog(ELogLevel.LOG_INFO, "reload cs name=" + t.Name);
                }
            }
            catch (Exception ex)
            {
                Log.WriteLog(ELogLevel.LOG_ERROR, ex.Message);
            }
        }

        public object Invoke(string scope, string functionName, params object[] args)
        {
            object sp = GetScope(scope);
            return sp.GetType().InvokeMember(functionName, BindingFlags.InvokeMethod, null, sp, args);
        }

        public dynamic GetScope(string className)
        {
            if (_scopes.ContainsKey(className))
            {
                return _scopes[className];
            }
            return null;
        }

        public void Dispose()
        {
            if(_fileWatcher != null)
            {
                _fileWatcher.Dispose();
                _scopes.Clear();
            }
        }
         
    }
}
